#include "os.h"

void plic_init(void)
{
	int hart = r_tp();
  
	/* 
	 * 为 UART0 设置优先级。
	 *
	 * 每个 PLIC 中断源都可以通过写入其 32 位的内存映射优先级寄存器来分配优先级。
	 * QEMU-virt（与 FU540-C000 一致）支持 7 个优先级。
	 * 优先级值 0 表示“永不产生中断”，等同于禁用该中断。
	 * 优先级 1 是最低的有效优先级，优先级 7 最高。
	 * 若多个全局中断优先级相同，则按中断 ID 再次区分；
	 * ID 较小的具有更高的实际优先级。
	 */
	*(uint32_t*)PLIC_PRIORITY(UART0_IRQ) = 1;
 
	/*
	 * 使能 UART0
	 *
	 * 通过在 Enable 寄存器中设置相应的比特位，可以使能每个全局中断。
	 */
	*(uint32_t*)PLIC_MENABLE(hart, UART0_IRQ) |= (1 << (UART0_IRQ % 32));

	/* 
	 * 为该 hart 设置优先级阈值（Priority Threshold）。
	 *
	 * PLIC 会屏蔽优先级小于或等于阈值的所有中断。
	 * 最大阈值为 7。
	 * 例如，阈值为 0 时，允许所有非零优先级的中断；阈值为 7 时，屏蔽所有中断。
	 * 注意：阈值是针对 PLIC（每个目标）的全局阈值，而不是针对每个中断源分别设置。
	 */
	*(uint32_t*)PLIC_MTHRESHOLD(hart) = 0;

	/* 使能机器模式外部中断。 */
	w_mie(r_mie() | MIE_MEIE);

	/* 使能机器模式全局中断。 */
	w_mstatus(r_mstatus() | MSTATUS_MIE);
}

/* 
 * 说明（DESCRIPTION）：
 *	向 PLIC 查询需要服务的中断。
 *	通过读取 claim 寄存器执行“中断认领”，它会返回最高优先级的挂起中断 ID，
 *	若没有挂起中断则返回 0。
 *	一次成功的认领还会**原子地**清除对应中断源的 pending 位。
 * 返回值（RETURN VALUE）：
 *	最高优先级挂起中断的 ID；若无挂起中断则返回 0。
 */
int plic_claim(void)
{
	int hart = r_tp();
	int irq = *(uint32_t*)PLIC_MCLAIM(hart);
	return irq;
}

/* 
 * 说明（DESCRIPTION）：
 *	将从 claim 得到的中断 ID（irq）写回 complete 寄存器，
 *	用于通知 PLIC：该中断已被服务完成。
 *	PLIC 不会检查完成 ID 是否与上次该目标的 claim ID 一致。
 *	如果完成 ID 不对应于当前为该目标使能的某个中断源，此“完成”会被悄然忽略。
 * 返回值（RETURN VALUE）：无
 */
void plic_complete(int irq)
{
	int hart = r_tp();
	*(uint32_t*)PLIC_MCOMPLETE(hart) = irq;
}
